/**
 * Copyright (c) 2015, 漆夜
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef REDIS_WRAPPER
#define REDIS_WRAPPER

#include <list>
#include <map>
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
#include "externl/include/msgpack.hpp"
#include "externl/include/hiredis/hiredis.h"


void show_reply(const redisReply *redis_reply);

template <class ValueType>
void value_reply(const redisReply *redis_reply, std::list<ValueType> &val)
{
    ValueType v;
    switch (redis_reply->type) {
    case REDIS_REPLY_INTEGER:
        val.push_back(redis_reply->integer);
        break;
    case REDIS_REPLY_STRING:
    {
        if (sizeof(ValueType) == redis_reply->len)
        {
            ::memcpy(&v, redis_reply->str, redis_reply->len);
            val.push_back(v);
        }

        break;
    }

    case REDIS_REPLY_ARRAY:
        for (size_t var = 0; var < redis_reply->elements; ++var)
            value_reply(redis_reply->element[var], val);
        break;

    default:
        std::cout << "unknow type" << std::endl;
        break;
    }
}

namespace redis {

/**
 * @brief ok 判断链接是否有问题，或处理是否失败
 * @param redis_handle redis的链接句柄
 * @return true: 成功; false: 失败
 * @post redis::error_str
 */
bool ok(redisContext *redis_handle);

/**
 * @brief error_str 获取错误信息
 * @param redis_handle redis的链接句柄
 * @return 错误日志
 * @pre reids::ok
 */
const char *error_str(redisContext *redis_handle);

namespace reply {
/**
 * @brief is_string 判断redis服务器返回的是否是一个流数据
 * @param reply 应答包
 * @return true:是; false:不是
 */
bool is_string(const redisReply *reply);

/**
 * @brief is_integer 判断redis服务器返回的是否是一个数字
 * @param reply 应答包
 * @return true:是; false:不是
 */
bool is_integer(const redisReply *reply);

/**
 * @brief is_array 判断redis服务器返回的是否是一个可变类型数组结构
 * @param reply 应答包
 * @return true:是; false:不是
 */
bool is_array(const redisReply *reply);

/**
 * @brief is_nil
 * @param reply
 * @return
 */
bool is_nil(const redisReply *reply);

/**
 * @brief is_status
 * @param reply
 * @return
 */
bool is_status(const redisReply *reply);

/**
 * @brief is_error
 * @param reply
 * @return
 */
bool is_error(const redisReply *reply);

}

namespace wrapper {

/**
 * @brief The RedisReply class 应答数据的包装，用于展示和数据结构转换
 * @attention 内部是用智能指针保持reply的handle有效
 * @attention 所有接口都是const接口，仅提供读操作
 */
class RedisReply
{
public:
    RedisReply();
    explicit RedisReply(redisReply *reply);

    ~RedisReply();

    RedisReply & operator = (redisReply *reply);

    /**
     * @brief is_vaild 判断变量是否有效
     * @return true:有效; false: 无效
     */
    bool is_vaild() const;

    /**
     * @brief get_msgpack 获取序列化后的msgpack对象
     * @return 是否获取到数据
     */
    template<class ValueType>
    bool get_msgpack_tuple(ValueType &obj) const {

        if (redis::reply::is_string(m_reply)) {
            msgpack::unpacked result;
            msgpack::unpack(result, m_reply->str, m_reply->len);
            result.get().convert(&obj);
            return true;
        }
        return false;
    }

    /**
      * @brief get_msgpack_list 获取序列化后的msgpack对象链表
      * @param 出参，存储反序列化后的数据链表
      * @return 是否获取到数据
      */
    template<class ValueType>
    bool get_msgpack_list(std::list<ValueType> &result_list) const {

        msgpack::unpacked result;
        ValueType val;
        if (redis::reply::is_array(m_reply)) {
            for (size_t var = 0; var < m_reply->elements; ++var) {
                if (redis::reply::is_string(m_reply->element[var])) {
                    msgpack::unpack(result, m_reply->element[var]->str, m_reply->element[var]->len);
                    result.get().convert(&val);
                    result_list.push_back(val);
                }
            }
            return true;
        }

        return false;
    }

    template<class KeyType, class ValueType>
    bool get_msgpack_map(std::map<KeyType, ValueType> result_map) const {
        // 返回map的时候是使用数组返回的，key->value 因此必须是2的倍数
        if (redis::reply::is_array(m_reply) && (m_reply->len % 2 ==0)) {
            msgpack::unpacked key;
            msgpack::unpacked value;
            for (size_t var = 0; var < m_reply->elements; ++var) {
                if (!redis::reply::is_string(m_reply->element[var])) {
                    result_map.clear();
                    return false;
                }

                if ((var % 2) == 0) {
                    msgpack::unpack(key, m_reply->element[var]->str, m_reply->element[var]->len);
                } else {
                    msgpack::unpack(value, m_reply->element[var]->str, m_reply->element[var]->len);
                    KeyType k;
                    ValueType v;
                    key.get().convert(&k);
                    value.get().convert(&v);
                    result_map.insert(std::make_pair(k, v));
                }
            }
            return true;
        }
        return false;
    }

    /**
     * @brief get_string 获取字节流
     * @return 字节流信息
     */
    std::string get_string() const;

    /**
     * @brief get_integer 获取integer
     * @return integer
     */
    long long get_integer() const;

    /**
     * @brief is_msgpack_error 判断msgpack是否失败
     * @return true: 是; false: 不是
     */
    bool is_msgpack_error() const;

    /**
     * @brief get_string_array 获取字符串数组
     * @return
     */
    std::vector<std::string> get_string_array() const;

    /**
     * @brief get_error 获取reply返回的错误
     * @return
     */
    std::string get_error() const;

    /**
     * @brief c_data 获取底层的hiredis的replay指针
     * @return replay指针
     */
    redisReply *c_data() const;

private:
    /// @brief 禁止拷贝构造和赋值方法
    RedisReply(RedisReply &cp);
    RedisReply & operator = (RedisReply &cp);
private:
    /// @brief redis应答对象c版本
    redisReply* m_reply;
    /// @brief 当前数据是否共享
    int8_t      m_is_share: 1;
    /// @brief 当前数据是否为空
    int8_t      m_is_null: 1;
    /// @brief 消息包unpack是否出错
    int8_t      m_is_msgpack_error: 1;
    /// @brief 当前数据使用数量
    int64_t     m_use_count;
};

/**
 * @brief The RedisConnecter class 通讯包装类
 */
class RedisConnecter
{
public:
    /**
     * @brief RedisConnecter 默认构造函数
     */
    RedisConnecter();

    /**
     * @brief RedisConnecter 带参数的构造函数
     * @param ip redis服务端ip
     * @param port redis服务端监听的端口
     * @param is_shared 链接是否共享
     * @param auto_disconnect 析构对象时是否自动断开链接
     */
    RedisConnecter(const char *ip, int32_t port = 6379, bool is_shared = false, bool auto_disconnect = true);

    /**
     * @brief ~RedisConnecter 析构对象
     */
    virtual ~RedisConnecter();

    /**
     * @brief connect 根据带参数的构造函数的配置信息进行数据库链接
     * @return true:链接成功; false: 链接失败
     */
    bool connect();

    /**
     * @brief connect 链接redis数据库
     * @param ip 数据库ip
     * @param port 数据库端口
     * @param is_shared 时候共享链接
     * @return true:链接成功; false: 链接失败
     * @attention 当原有链接已经建立时，断开原有链接，并建立新的链接
     */
    bool connect(const char *ip, int32_t port = 6379, bool is_shared = false, bool auto_disconnect = true);

    /**
     * @brief disconnect 断开数据库链接
     */
    void disconnect();

    /**
     * @brief ok 判断通讯是否正常
     * @return
     */
    bool ok();

    /**
     * @brief is_owner 判断链接对象是否是你所拥有的
     * @return
     */
    bool is_owner();

    /**
     * @brief is_shared 链接是否是共享的
     * @return
     */
    bool is_shared();

    /**
     * @brief error_str
     * @return
     */
    const char *error_str();

    /**
     * @brief msgpack_error 设置msgpack是否解包和组包错误的标记
     * @param is 标记
     * @return true: 发生了错误，false：未发生错误
     */
    void msgpack_error(bool is);
    bool msgpack_error();

    /**
     * @brief get_context 获取hiredis的上下文
     * @return 上下文句柄
     */
    redisContext *get_context();

    /**
     * @brief cmd 执行rudis命令
     * @param redis_cmd 命令行
     * @return 处理结构
     * @note 当前接口会内部打印返回信息到控制台
     */
    bool cmd(const char *redis_cmd, ...);

private:
    /**
     * @breif 禁止使用拷贝构造函数和赋值函数
     */
    RedisConnecter(const RedisConnecter &cp);
    RedisConnecter &operator = (const RedisConnecter &cp);
private:
    std::string     m_ip;
    int32_t         m_port;
    bool            m_is_shared;
    bool            m_auto_disconnect;
    bool            m_is_msgpack_error;
    redisContext*   m_redis_context;
};

/**
 * @brief The RedisCmder class
 */
class RedisCmder {
    /**
     * @brief run_cmd
     * @param connecter
     * @param redis_cmd
     * @return
     */
    bool run_cmd(RedisConnecter *connecter, const char *redis_cmd, ...);
    redisReply *run_cmd_c(RedisConnecter *connecter, const char *redis_cmd, ...);

    /**
     * @brief get_string
     * @return
     */
    std::string get_string();

    /**
     * @brief get_number
     * @return
     */
    int32_t get_number();
};

class RedisResult {
public:
    RedisResult();
    virtual ~RedisResult();

    /**
     * @brief is_connect_error 判断是否链接错误
     * @return true: error; false: normul
     */
    bool is_connect_error();

    /**
     * @brief is_reply_error 是否reply错误
     * @return true: error; false: normul
     */
    bool is_reply_error();

    /**
     * @brief is_msgpack_error 是否是unpack错误
     * @return true: error; false: normul
     */
    bool is_msgpack_error();

    /**
     * @brief error_str
     * @return
     */
    std::string error_str();

    /**
     * @brief error_id
     * @return 0 成功；!0 失败
     */
    int32_t error_id();

private:
    int32_t m_error_id;
    std::string m_error_str;
};
}
namespace keys {

}
namespace string {

}
namespace sorted_sets {

}
namespace psub {

}
namespace transactions {

/**
 * @brief multi
 * @param redis_handle
 * @return
 */
bool multi(redisContext *redis_handle);

/**
 * @brief exec
 * @param redis_handle
 * @return
 */
bool exec(redisContext *redis_handle);

/**
 * @brief discard
 * @param redis_handle
 * @return
 */
bool discard(redisContext *redis_handle);

/**
 * @brief watch
 * @param redis_handle
 * @return
 */
bool watch(redisContext *redis_handle);

/**
 * @brief unwatch
 * @param redis_handle
 * @return
 */
bool unwatch(redisContext *redis_handle);
}
namespace scripting {

}
namespace connection {

/**
 * @brief auth 设置鉴权信息
 * @param redis_handle 链接句柄
 * @param passwd 密码
 * @return 鉴权结果
 */
bool auth(redisContext *redis_handle, const char *passwd, std::string *err_str = NULL);

/**
 * @brief ping
 * @param redis_handle
 * @param ping_str
 * @return
 */
std::string ping(redisContext *redis_handle, const char *ping_str = "");

/**
 * @brief echo
 * @param redis_handle
 * @param echo_str
 * @return
 */
std::string echo(redisContext *redis_handle, const char *echo_str = "hello world");

/**
 * @brief select
 * @param redis_handle
 * @param ii
 * @return
 */
bool select (redisContext *redis_handle, uint32_t ii);

/**
 * @brief quit
 * @param redis_handle
 */
void quit(redisContext *redis_handle);

}
namespace server {

bool flushall(redisContext *redis_handle);

void info(redisContext *redis_handle);

time_t lastsave(redisContext *redis_handle);

}
namespace lists {

template <class ValueType>
bool lpush(redisContext *redis_handle, const char *key, ValueType val) {
    if (NULL == redis_handle) {
        return false;
    }

    // 使用msgpcak进行序列化
    std::stringstream ss;
    msgpack::pack(ss, val);

    redis::wrapper::RedisReply reply ((redisReply *) redisCommand(
                                          redis_handle,
                                          "lpush %s %b",
                                          key, ss.str().c_str(), ss.str().length()));
    if (!redis::ok(redis_handle)) {
        std::cout << redis::error_str(redis_handle) << std::endl;
        return false;
    }

    if (!reply.is_vaild()) {
        return false;
    }

#ifdef CLIENT_DEBUG
    show_reply(reply.c_data());
#endif
    return true;
}

template <class ValueType>
std::list<ValueType> rpop(redisContext *redis_handle, const char *key) {
    std::list<ValueType> valist;
    if (NULL == redis_handle) {
        return valist;
    }

    redis::wrapper::RedisReply reply ((redisReply *) redisCommand(redis_handle, "rpop %s", key));
    if (!redis::ok(redis_handle)) {
        std::cout << redis::error_str(redis_handle) << std::endl;
        return valist;
    }

    if (!reply.is_vaild()) {
        return valist;
    }

    value_reply(reply.c_data(), valist);

    return valist;
}

}
namespace sets {

template <class ValueType>
bool sadd(redisContext *redis_handle, const char *key, ValueType val) {
    if (NULL == redis_handle) {
        return false;
    }

    std::stringstream ss;
    msgpack::pack(ss, val);

    redis::wrapper::RedisReply reply ((redisReply *) redisCommand(
                                          redis_handle,
                                          "sadd %s %b",
                                          key, ss.str().c_str(), ss.str().length()));
    if (!redis::ok(redis_handle)) {
        std::cout << redis::error_str(redis_handle) << std::endl;
        return false;
    }

    if (!reply.is_vaild()) {
        return false;
    }

#ifdef CLIENT_DEBUG
    show_reply(reply.c_data());
#endif
    return true;
}

void srem(redisContext *redis_handle, const char *key);

template <class ValueType>
std::list<ValueType> smembers(redisContext *redis_handle, const char *key) {
    std::list<ValueType> valist;
    if (NULL == redis_handle) {
        return valist;
    }

    redis::wrapper::RedisReply reply ((redisReply *) redisCommand(redis_handle, "smembers %s", key));
    if (!redis::ok(redis_handle)) {
        std::cout << redis::error_str(redis_handle) << std::endl;
        return false;
    }

    if (!reply.is_vaild()) {
        return valist;
    }

#ifdef CLIENT_DEBUG
    value_reply(reply.c_data(), valist);
#endif
    return valist;
}

template <class ValueType>
std::list<ValueType> spop(redisContext *redis_handle, const char *key, size_t pop_count)
{
    std::list<ValueType> valist;
    if (NULL == redis_handle) {
        return valist;
    }

    redis::wrapper::RedisReply reply ((redisReply *) redisCommand(redis_handle, "spop %s %d", key, pop_count));
    if (!redis::ok(redis_handle)) {
        std::cout << redis::error_str(redis_handle) << std::endl;
        return valist;
    }

    if (!reply.is_vaild()) {
        return valist;
    }

    // 将数据压倒链表中进行返回
    reply.get_msgpack_list(valist);

#ifdef CLIENT_DEBUG
    value_reply(reply.c_data(), valist);
#endif
    return valist;
}

template <class ValueType>
bool sismember(redisContext *redis_handle, const char *key, ValueType val)
{
    if (NULL == redis_handle) {
        return false;
    }

    std::stringstream ss;
    msgpack::pack(ss, val);

    redis::wrapper::RedisReply reply ((redisReply *) redisCommand(
                                          redis_handle,
                                          "sismember %s %b",
                                          key, ss.str().c_str(), ss.str().length()));
    if (!redis::ok(redis_handle)) {
        std::cout << redis::error_str(redis_handle) << std::endl;
        return false;
    }

    if (!reply.is_vaild()) {
        return false;
    }

    if (reply.c_data()->type == REDIS_REPLY_INTEGER)
    {
        return 1 == reply.c_data()->integer;
    }

    return false;
}

}
namespace hashs {

/**
 * @brief 设置 key 指定的哈希集中指定字段的值。
 *        如果 key 指定的哈希集不存在，会创建一个新的哈希集并与 key 关联。如果字段在哈希集中存在，它将被重写。
 * @param FiledType 应该是一个msgpack::type::tuple类型或者是一个可以进行msgpack序列化的类型
 * @param ValueType 应该是一个msgpack::type::tuple类型或者是一个可以进行msgpack序列化的类型
 * @param redis_handle redis通讯对象指针
 * @param key key-hashs结构中的key
 * @param field hashs中的子项
 * @param val hashs中子项对应的值
 * @return true: 执行成功; false: 执行失败
 * @attention
 */
template <class FieldType, class ValueType>
bool hset(redisContext *redis_handle, const char *key, FieldType field, ValueType val) {
    if (NULL == redis_handle) {
        return false;
    }

    // 对field进行序列化
    std::stringstream ss_field;
    msgpack::pack(ss_field, field);
    // 对value进行序列化
    std::stringstream ss_value;
    msgpack::pack(ss_value, val);

    // 执行hset
    redis::wrapper::RedisReply reply ((redisReply *) redisCommand(
                                          redis_handle, "hset %s %b %b",
                                          key,
                                          ss_field.str().c_str(), ss_field.str().length(),
                                          ss_value.str().c_str(), ss_value.str().length()
                                          )
                                      );

    if (!redis::ok(redis_handle)) {
#ifdef CLIENT_DEBUG
        std::cout << redis::error_str(redis_handle) << std::endl;
#endif
        return false;
    }

    if (!reply.is_vaild()) {
        return false;
    }
#ifdef CLIENT_DEBUG
    show_reply(reply.c_data());
#endif
    return true;
}

template <>
bool hset(redisContext *redis_handle, const char *key, std::string field, std::string val);
/**
 * @brief hdel 从 key 指定的哈希集中移除指定的域。
 *        在哈希集中不存在的域将被忽略。如果 key 指定的哈希集不存在，它将被认为是一个空的哈希集，该命令将返回0。
 * @param FiledType 应该是一个msgpack::type::tuple类型或者是一个可以进行msgpack序列化的类型
 * @param ValueType 应该是一个msgpack::type::tuple类型或者是一个可以进行msgpack序列化的类型
 * @param redis_handle redis通讯对象指针
 * @param key key-hashs结构中的key
 * @param field hashs中的子项
 * @return true: 执行成功; false: 执行失败
 * @attention 当反序列化失败后,将抛出类型错误的异常,外部需要对异常进行捕获
 */
template <class FieldType>
bool hdel(redisContext *redis_handle, const char *key, const FieldType &field) {
    if (NULL == redis_handle) {
        return false;
    }

    // 对field进行序列化
    std::stringstream ss_field;
    msgpack::pack(ss_field, field);

    redis::wrapper::RedisReply reply((redisReply *) redisCommand(
                                         redis_handle,
                                         "hdel %s %b",
                                         key,
                                         ss_field.str().c_str(), ss_field.str().length()));
    if (!redis::ok(redis_handle)) {
#ifdef CLIENT_DEBUG
        std::cout << redis::error_str(redis_handle) << std::endl;
#endif
        return false;
    }

    if (!reply.is_vaild()) {
        return false;
    }
#ifdef CLIENT_DEBUG
    show_reply(reply.c_data());
#endif

    return true;
}

/**
 * @brief hset 对key-hashs的单个key-field只是value
 * @param ValueType 应该是一个msgpack::type::tuple类型或者是一个可以进行msgpack序列化的类型
 * @param FiledType 应该是一个msgpack::type::tuple类型或者是一个可以进行msgpack序列化的类型
 * @param redis_handle redis通讯对象指针
 * @param key key-hashs结构中的key
 * @param field hashs中的子项
 * @return 返回value的数据，当field不存在则返回value的默认数据
 */
template<class FieldType, class ValueType>
bool hget(redisContext *redis_handle, const char *key, const FieldType &field, ValueType &val) {

    if (NULL == redis_handle) {
        return false;
    }

    // 对field进行序列化
    std::stringstream ss_field;
    msgpack::pack(ss_field, field);

    redis::wrapper::RedisReply reply((redisReply*)redisCommand(
                                         redis_handle,
                                         "hget %s %b",
                                         key,
                                         ss_field.str().c_str(), ss_field.str().length()));

    if (!redis::ok(redis_handle)) {
#ifdef CLIENT_DEBUG
        std::cout << redis::error_str(redis_handle) << std::endl;
#endif
        return false;
    }

    if (!reply.is_vaild()) {
        return false;
    }

    if (reply.get_msgpack_tuple(val)) {
        return true;
    }

    return false;
}

template <>
bool hget(redisContext *redis_handle, const char *key, const std::string &field, std::string &val);
/**
 * @brief hgetall 返回 key 指定的哈希集中所有的字段和值。返回值中，每个字段名的下一个是它的值，所以返回值的长度是哈希集大小的两倍
 * @param FiledType 应该是一个msgpack::type::tuple类型或者是一个可以进行msgpack序列化的类型
 * @param ValueType 应该是一个msgpack::type::tuple类型或者是一个可以进行msgpack序列化的类型
 * @param redis_handle redis通讯对象指针
 * @param key key-hashs结构中的key
 * @return
 * @warning 要求field和value的值得类型是一致的
 */
template<class ValueType>
std::list<ValueType> hgetall(redisContext *redis_handle, const char *key) {
    std::list<ValueType> valist;
    if (NULL == redis_handle) {
        return valist;
    }

    redis::wrapper::RedisReply reply((redisReply*)redisCommand(redis_handle, "hgetall %s", key));

    if (!redis::ok(redis_handle)) {
#ifdef CLIENT_DEBUG
        std::cout << redis::error_str(redis_handle) << std::endl;
#endif
        return valist;
    }

    reply.get_msgpack_list(valist);

    return valist;
}

// 当x内使用"<>"模版定义时需要先typedef一个类型
#define REDIS_PAIR(X) class FieldType##X, class ValueType##X
#define PARAM_PAIR(X) FieldType##X field##X, ValueType##X val##X

#define FIELD(X) field##X
#define VALUE(X) val##X

#define MSGPACK_PACK_FIELD(X) \
    std::stringstream ss_field##X; \
    msgpack::pack(ss_field##X, FIELD(X))

#define MSGPACK_PACK_VALUE(X) \
    std::stringstream ss_value##X; \
    msgpack::pack(ss_value##X, VALUE(X))

#define MSGPACK_PACK_PAIR(X) \
    MSGPACK_PACK_FIELD(X); \
    MSGPACK_PACK_VALUE(X)

#define MSGPACKSTREAM_FIELD(X) ss_field##X.str()
#define MSGPACKSTREAM_VALUE(X) ss_value##X.str()

#define MSGPACKSTREAM_PAIR(X) \
    MSGPACKSTREAM_FIELD(X).c_str(), MSGPACKSTREAM_FIELD(X).length(), \
    MSGPACKSTREAM_VALUE(X).c_str(), MSGPACKSTREAM_VALUE(X).length()

template< REDIS_PAIR(1) >
bool hmset(redisContext *redis_handle, const char *key, PARAM_PAIR(1)) {

    if (NULL == redis_handle) {
        return false;
    }

    MSGPACK_PACK_PAIR(1);

    // 执行hset
    redis::wrapper::RedisReply reply ((redisReply *) redisCommand(
                                          redis_handle, "hmset %s %b %b",
                                          key,
                                          MSGPACKSTREAM_PAIR(1)
                                          )
                                      );

    if (!redis::ok(redis_handle)) {
#ifdef CLIENT_DEBUG
        std::cout << redis::error_str(redis_handle) << std::endl;
#endif
        return false;
    }

    if (!reply.is_vaild()) {
        return false;
    }
#ifdef CLIENT_DEBUG
    show_reply(reply.c_data());
#endif
    return true;
}

template< REDIS_PAIR(1), REDIS_PAIR(2) >
bool hmset(redisContext *redis_handle, const char *key,
           PARAM_PAIR(1), PARAM_PAIR(2)) {

    if (NULL == redis_handle) {
        return false;
    }

    MSGPACK_PACK_PAIR(1);
    MSGPACK_PACK_PAIR(2);

    // 执行hset
    redis::wrapper::RedisReply reply ((redisReply *) redisCommand(
                                          redis_handle, "hmset %s %b %b %b %b",
                                          key,
                                          MSGPACKSTREAM_PAIR(1),
                                          MSGPACKSTREAM_PAIR(2)
                                          )
                                      );

    if (!redis::ok(redis_handle)) {
#ifdef CLIENT_DEBUG
        std::cout << redis::error_str(redis_handle) << std::endl;
#endif
        return false;
    }

    if (!reply.is_vaild()) {
        return false;
    }
#ifdef CLIENT_DEBUG
    show_reply(reply.c_data());
#endif
    return true;
}

template< REDIS_PAIR(1), REDIS_PAIR(2), REDIS_PAIR(3) >
bool hmset(redisContext *redis_handle, const char *key,
           PARAM_PAIR(1), PARAM_PAIR(2), PARAM_PAIR(3)) {
    if (NULL == redis_handle) {
        return false;
    }

    MSGPACK_PACK_PAIR(1);
    MSGPACK_PACK_PAIR(2);
    MSGPACK_PACK_PAIR(3);

    // 执行hset
    redis::wrapper::RedisReply reply ((redisReply *) redisCommand(
                                          redis_handle, "hmset %s %b %b %b %b %b %b",
                                          key,
                                          MSGPACKSTREAM_PAIR(1),
                                          MSGPACKSTREAM_PAIR(2),
                                          MSGPACKSTREAM_PAIR(3)
                                          )
                                      );

    if (!redis::ok(redis_handle)) {
#ifdef CLIENT_DEBUG
        std::cout << redis::error_str(redis_handle) << std::endl;
#endif
        return false;
    }

    if (!reply.is_vaild()) {
        return false;
    }
#ifdef CLIENT_DEBUG
    show_reply(reply.c_data());
#endif
    return true;
}

template< REDIS_PAIR(1),REDIS_PAIR(2), REDIS_PAIR(3), REDIS_PAIR(4) >
bool hmset(redisContext *redis_handle, const char *key,
           PARAM_PAIR(1), PARAM_PAIR(2),PARAM_PAIR(3), PARAM_PAIR(4)) {
    if (NULL == redis_handle) {
        return false;
    }

    MSGPACK_PACK_PAIR(1);
    MSGPACK_PACK_PAIR(2);
    MSGPACK_PACK_PAIR(3);
    MSGPACK_PACK_PAIR(4);

    // 执行hset
    redis::wrapper::RedisReply reply ((redisReply *) redisCommand(
                                          redis_handle, "hmset %s %b %b %b %b %b %b %b %b",
                                          key,
                                          MSGPACKSTREAM_PAIR(1),
                                          MSGPACKSTREAM_PAIR(2),
                                          MSGPACKSTREAM_PAIR(3),
                                          MSGPACKSTREAM_PAIR(4)
                                          )
                                      );

    if (!redis::ok(redis_handle)) {
#ifdef CLIENT_DEBUG
        std::cout << redis::error_str(redis_handle) << std::endl;
#endif
        return false;
    }

    if (!reply.is_vaild()) {
        return false;
    }
#ifdef CLIENT_DEBUG
    show_reply(reply.c_data());
#endif
    return true;
}

template< REDIS_PAIR(1), REDIS_PAIR(2), REDIS_PAIR(3), REDIS_PAIR(4), REDIS_PAIR(5) >
bool hmset(redisContext *redis_handle, const char *key,
           PARAM_PAIR(1), PARAM_PAIR(2), PARAM_PAIR(3), PARAM_PAIR(4), PARAM_PAIR(5)) {
    if (NULL == redis_handle) {
        return false;
    }

    MSGPACK_PACK_PAIR(1);
    MSGPACK_PACK_PAIR(2);
    MSGPACK_PACK_PAIR(3);
    MSGPACK_PACK_PAIR(4);
    MSGPACK_PACK_PAIR(5);

    // 执行hset
    redis::wrapper::RedisReply reply ((redisReply *) redisCommand(
                                          redis_handle, "hmset %s %b %b %b %b %b %b %b %b %b %b",
                                          key,
                                          MSGPACKSTREAM_PAIR(1),
                                          MSGPACKSTREAM_PAIR(2),
                                          MSGPACKSTREAM_PAIR(3),
                                          MSGPACKSTREAM_PAIR(4),
                                          MSGPACKSTREAM_PAIR(5)
                                          )
                                      );

    if (!redis::ok(redis_handle)) {
#ifdef CLIENT_DEBUG
        std::cout << redis::error_str(redis_handle) << std::endl;
#endif
        return false;
    }

    if (!reply.is_vaild()) {
        return false;
    }
#ifdef CLIENT_DEBUG
    show_reply(reply.c_data());
#endif
    return true;
}

template< REDIS_PAIR(1), REDIS_PAIR(2), REDIS_PAIR(3), REDIS_PAIR(4), REDIS_PAIR(5), REDIS_PAIR(6) >
bool hmset(redisContext *redis_handle, const char *key,
           PARAM_PAIR(1), PARAM_PAIR(2), PARAM_PAIR(3), PARAM_PAIR(4), PARAM_PAIR(5), PARAM_PAIR(6)) {

    if (NULL == redis_handle) {
        return false;
    }

    MSGPACK_PACK_PAIR(1);
    MSGPACK_PACK_PAIR(2);
    MSGPACK_PACK_PAIR(3);
    MSGPACK_PACK_PAIR(4);
    MSGPACK_PACK_PAIR(5);
    MSGPACK_PACK_PAIR(6);
    // 执行hset
    redis::wrapper::RedisReply reply ((redisReply *) redisCommand(
                                          redis_handle, "hmset %s %b %b %b %b %b %b %b %b %b %b %b %b",
                                          key,
                                          MSGPACKSTREAM_PAIR(1),
                                          MSGPACKSTREAM_PAIR(2),
                                          MSGPACKSTREAM_PAIR(3),
                                          MSGPACKSTREAM_PAIR(4),
                                          MSGPACKSTREAM_PAIR(5),
                                          MSGPACKSTREAM_PAIR(6)
                                          )
                                      );

    if (!redis::ok(redis_handle)) {
#ifdef CLIENT_DEBUG
        std::cout << redis::error_str(redis_handle) << std::endl;
#endif
        return false;
    }

    if (!reply.is_vaild()) {
        return false;
    }
#ifdef CLIENT_DEBUG
    show_reply(reply.c_data());
#endif
    return true;

}

template< REDIS_PAIR(1), REDIS_PAIR(2), REDIS_PAIR(3), REDIS_PAIR(4), REDIS_PAIR(5), REDIS_PAIR(6), REDIS_PAIR(7) >
bool hmset(redisContext *redis_handle, const char *key,
           PARAM_PAIR(1), PARAM_PAIR(2), PARAM_PAIR(3), PARAM_PAIR(4), PARAM_PAIR(5), PARAM_PAIR(6), PARAM_PAIR(7)) {

    if (NULL == redis_handle) {
        return false;
    }

    MSGPACK_PACK_PAIR(1);
    MSGPACK_PACK_PAIR(2);
    MSGPACK_PACK_PAIR(3);
    MSGPACK_PACK_PAIR(4);
    MSGPACK_PACK_PAIR(5);
    MSGPACK_PACK_PAIR(6);
    MSGPACK_PACK_PAIR(7);
    // 执行hset
    redis::wrapper::RedisReply reply ((redisReply *) redisCommand(
                                          redis_handle, "hmset %s %b %b %b %b %b %b %b %b %b %b %b %b %b %b",
                                          key,
                                          MSGPACKSTREAM_PAIR(1),
                                          MSGPACKSTREAM_PAIR(2),
                                          MSGPACKSTREAM_PAIR(3),
                                          MSGPACKSTREAM_PAIR(4),
                                          MSGPACKSTREAM_PAIR(5),
                                          MSGPACKSTREAM_PAIR(6),
                                          MSGPACKSTREAM_PAIR(7)
                                          )
                                      );

    if (!redis::ok(redis_handle)) {
#ifdef CLIENT_DEBUG
        std::cout << redis::error_str(redis_handle) << std::endl;
#endif
        return false;
    }

    if (!reply.is_vaild()) {
        return false;
    }
#ifdef CLIENT_DEBUG
    show_reply(reply.c_data());
#endif
    return true;

}

template< REDIS_PAIR(1), REDIS_PAIR(2), REDIS_PAIR(3), REDIS_PAIR(4), REDIS_PAIR(5), REDIS_PAIR(6), REDIS_PAIR(7), REDIS_PAIR(8) >
bool hmset(redisContext *redis_handle, const char *key,
           PARAM_PAIR(1), PARAM_PAIR(2), PARAM_PAIR(3), PARAM_PAIR(4), PARAM_PAIR(5), PARAM_PAIR(6), PARAM_PAIR(7), PARAM_PAIR(8)) {

    if (NULL == redis_handle) {
        return false;
    }

    MSGPACK_PACK_PAIR(1);
    MSGPACK_PACK_PAIR(2);
    MSGPACK_PACK_PAIR(3);
    MSGPACK_PACK_PAIR(4);
    MSGPACK_PACK_PAIR(5);
    MSGPACK_PACK_PAIR(6);
    MSGPACK_PACK_PAIR(7);
    MSGPACK_PACK_PAIR(8);
    // 执行hset
    redis::wrapper::RedisReply reply ((redisReply *) redisCommand(
                                          redis_handle, "hmset %s %b %b %b %b %b %b %b %b %b %b %b %b %b %b %b %b",
                                          key,
                                          MSGPACKSTREAM_PAIR(1),
                                          MSGPACKSTREAM_PAIR(2),
                                          MSGPACKSTREAM_PAIR(3),
                                          MSGPACKSTREAM_PAIR(4),
                                          MSGPACKSTREAM_PAIR(5),
                                          MSGPACKSTREAM_PAIR(6),
                                          MSGPACKSTREAM_PAIR(7),
                                          MSGPACKSTREAM_PAIR(8)
                                          )
                                      );

    if (!redis::ok(redis_handle)) {
#ifdef CLIENT_DEBUG
        std::cout << redis::error_str(redis_handle) << std::endl;
#endif
        return false;
    }

    if (!reply.is_vaild()) {
        return false;
    }
#ifdef CLIENT_DEBUG
    show_reply(reply.c_data());
#endif
    return true;

}

template< REDIS_PAIR(1), REDIS_PAIR(2), REDIS_PAIR(3), REDIS_PAIR(4), REDIS_PAIR(5), REDIS_PAIR(6), REDIS_PAIR(7), REDIS_PAIR(8), REDIS_PAIR(9) >
bool hmset(redisContext *redis_handle, const char *key,
           PARAM_PAIR(1), PARAM_PAIR(2), PARAM_PAIR(3), PARAM_PAIR(4), PARAM_PAIR(5), PARAM_PAIR(6), PARAM_PAIR(7), PARAM_PAIR(8), PARAM_PAIR(9)) {

    if (NULL == redis_handle) {
        return false;
    }

    MSGPACK_PACK_PAIR(1);
    MSGPACK_PACK_PAIR(2);
    MSGPACK_PACK_PAIR(3);
    MSGPACK_PACK_PAIR(4);
    MSGPACK_PACK_PAIR(5);
    MSGPACK_PACK_PAIR(6);
    MSGPACK_PACK_PAIR(7);
    MSGPACK_PACK_PAIR(8);
    MSGPACK_PACK_PAIR(9);
    // 执行hset
    redis::wrapper::RedisReply reply ((redisReply *) redisCommand(
                                          redis_handle, "hmset %s %b %b %b %b %b %b %b %b %b %b %b %b %b %b %b %b %b %b",
                                          key,
                                          MSGPACKSTREAM_PAIR(1),
                                          MSGPACKSTREAM_PAIR(2),
                                          MSGPACKSTREAM_PAIR(3),
                                          MSGPACKSTREAM_PAIR(4),
                                          MSGPACKSTREAM_PAIR(5),
                                          MSGPACKSTREAM_PAIR(6),
                                          MSGPACKSTREAM_PAIR(7),
                                          MSGPACKSTREAM_PAIR(8),
                                          MSGPACKSTREAM_PAIR(9)
                                          )
                                      );

    if (!redis::ok(redis_handle)) {
#ifdef CLIENT_DEBUG
        std::cout << redis::error_str(redis_handle) << std::endl;
#endif
        return false;
    }

    if (!reply.is_vaild()) {
        return false;
    }
#ifdef CLIENT_DEBUG
    show_reply(reply.c_data());
#endif
    return true;
}

template< REDIS_PAIR(1), REDIS_PAIR(2), REDIS_PAIR(3), REDIS_PAIR(4), REDIS_PAIR(5), REDIS_PAIR(6), REDIS_PAIR(7), REDIS_PAIR(8), REDIS_PAIR(9), REDIS_PAIR(10) >
bool hmset(redisContext *redis_handle, const char *key,
           PARAM_PAIR(1), PARAM_PAIR(2), PARAM_PAIR(3), PARAM_PAIR(4), PARAM_PAIR(5), PARAM_PAIR(6), PARAM_PAIR(7), PARAM_PAIR(8), PARAM_PAIR(9), PARAM_PAIR(10)) {

    if (NULL == redis_handle) {
        return false;
    }

    MSGPACK_PACK_PAIR(1);
    MSGPACK_PACK_PAIR(2);
    MSGPACK_PACK_PAIR(3);
    MSGPACK_PACK_PAIR(4);
    MSGPACK_PACK_PAIR(5);
    MSGPACK_PACK_PAIR(6);
    MSGPACK_PACK_PAIR(7);
    MSGPACK_PACK_PAIR(8);
    MSGPACK_PACK_PAIR(9);
    MSGPACK_PACK_PAIR(10);
    // 执行hset
    redis::wrapper::RedisReply reply ((redisReply *) redisCommand(
                                          redis_handle, "hmset %s %b %b %b %b %b %b %b %b %b %b %b %b %b %b %b %b %b %b %b %b",
                                          key,
                                          MSGPACKSTREAM_PAIR(1),
                                          MSGPACKSTREAM_PAIR(2),
                                          MSGPACKSTREAM_PAIR(3),
                                          MSGPACKSTREAM_PAIR(4),
                                          MSGPACKSTREAM_PAIR(5),
                                          MSGPACKSTREAM_PAIR(6),
                                          MSGPACKSTREAM_PAIR(7),
                                          MSGPACKSTREAM_PAIR(8),
                                          MSGPACKSTREAM_PAIR(9),
                                          MSGPACKSTREAM_PAIR(10)
                                          )
                                      );

    if (!redis::ok(redis_handle)) {
#ifdef CLIENT_DEBUG
        std::cout << redis::error_str(redis_handle) << std::endl;
#endif
        return false;
    }

    if (!reply.is_vaild()) {
        return false;
    }
#ifdef CLIENT_DEBUG
    show_reply(reply.c_data());
#endif
    return true;
}

/**
 * @brief 返回 key 指定的哈希集中所有字段的名字。
 * @param FiledType 应该是一个msgpack::type::tuple类型或者是一个可以进行msgpack序列化的类型
 * @param redis_handle redis通讯对象指针
 * @param key key-hashs结构中的key
 * @param field_list 子项列表，出参
 * @param true: 执行成功; false: 执行失败
 */
template<class FieldType>
bool hkeys(redisContext *redis_handle, const char *key, std::list<FieldType> &field_list) {
    if (NULL == redis_handle) {
        return false;
    }

    redis::wrapper::RedisReply reply((redisReply*)redisCommand(redis_handle, "hkeys %s", key));

    if (!redis::ok(redis_handle)) {
        std::cout << redis::error_str(redis_handle) << std::endl;
        return false;
    }

    if (!reply.is_vaild()) {
        return false;
    }

    if (!reply.get_msgpack_list(field_list)) {
        return false;
    }

#ifdef CLIENT_DEBUG
    show_reply(reply.c_data());
#endif
    return true;
}

/**
 * @brief hlen 返回 key 指定的哈希集包含的字段的数量。
 * @param redis_handle redis通讯对象指针
 * @param key key-hashs结构中的key
 * @return 当前key对应的hashs有多少个field
 */
std::size_t hlen(redisContext *redis_handle, const char* key);

/**
 * @brief hexists 返回字段是否是 key 指定的哈希集中存在的字段。
 * @param ValueType 应该是一个msgpack::type::tuple类型或者是一个可以进行msgpack序列化的类型
 * @param FiledType 应该是一个msgpack::type::tuple类型或者是一个可以进行msgpack序列化的类型
 * @param redis_handle redis通讯对象指针
 * @param key key-hashs结构中的key
 * @param field hashs中的子项
 * @return true: 是key中的field; false: 不是
 */
template<class FieldType>
bool hexists(redisContext *redis_handle, const char *key, FieldType field) {
    if (NULL == redis_handle) {
        return false;
    }

    // 对field进行序列化
    std::stringstream ss_field;
    msgpack::pack(ss_field, field);

    redis::wrapper::RedisReply reply((redisReply *) redisCommand(
                                         redis_handle,
                                         "hexists %s %b",
                                         key,
                                         ss_field.str().c_str(), ss_field.str().length()));
    if (!redis::ok(redis_handle)) {
#ifdef CLIENT_DEBUG
        std::cout << redis::error_str(redis_handle) << std::endl;
#endif
        return false;
    }

    if (1 == reply.get_integer()) {
        return true;
    }
#ifdef CLIENT_DEBUG
    show_reply(reply.c_data());
#endif

    return false;

}

/**
 * @brief hsetnx 只在 key 指定的哈希集中不存在指定的字段时，设置字段的值。
 *        如果 key 指定的哈希集不存在，会创建一个新的哈希集并与 key 关联。如果字段已存在，该操作无效果。
 * @param ValueType 应该是一个msgpack::type::tuple类型或者是一个可以进行msgpack序列化的类型
 * @param FiledType 应该是一个msgpack::type::tuple类型或者是一个可以进行msgpack序列化的类型
 * @param redis_handle redis通讯对象指针
 * @param key key-hashs结构中的key
 * @param field hashs中的子项
 * @return true: 是key中的field; false: 不是
 */
template<class FieldType, class ValueType>
bool hsetnx(redisContext *redis_handle, const char *key, FieldType field, ValueType val) {
    if (NULL == redis_handle) {
        return false;
    }

    // 对field进行序列化
    std::stringstream ss_field;
    msgpack::pack(ss_field, field);

    // 对value进行序列化
    std::stringstream ss_value;
    msgpack::pack(ss_value, val);

    redis::wrapper::RedisReply reply((redisReply *) redisCommand(
                                         redis_handle,
                                         "hexists %s %b %b",
                                         key,
                                         ss_field.str().c_str(), ss_field.str().length(),
                                         ss_value.str().c_str(), ss_value.str().length()));
    if (!redis::ok(redis_handle)) {
#ifdef CLIENT_DEBUG
        std::cout << redis::error_str(redis_handle) << std::endl;
#endif
        return false;
    }

    if (1 == reply.get_integer()) {
        return true;
    }
#ifdef CLIENT_DEBUG
    show_reply(reply.c_data());
#endif

    return false;
}

}
namespace run {

/**
 * @brief cmd 执行一个redis命令
 * @param redis_handle redis通讯的句柄
 * @param redis_cmd redis命令
 * @param ... 可变参数
 * @return true执行成功，false执行失败
 * @attention 获取执行失败的原因通过redis::error_str()获取错误原因
 */
bool cmd(redisContext *redis_handle, const char *redis_cmd, ...);

/**
 * @brief cmd 执行redis命令
 * @param ip redis服务器ip地址
 * @param port redis服务器port
 * @param err_str 出参，当发生错误时，将追加信息到err_str中
 * @param redis_cmd 命令
 * @param ... 可变参数
 * @return true执行成功，false执行失败
 */
bool cmd(const char *ip, int32_t port, std::string &err_str, const char *redis_cmd, ...);

}

}


#endif // REDIS_WRAPPER

